import __builtin__
from panda3d.core import Point3, AmbientLight, DirectionalLight, LightAttrib, Vec4
from client.gamestates.stateabstract import StateAbstract
from client.utils import assethandling
from client.unitsystem.unitmanager import UnitManager
from client.towersystem.towermanager import TowerManager
from client.bulletsystem.bulletmanager import BulletManager
from client.utils.enum import Enum
from networking.ganet.network import message
from direct.gui.OnscreenText import OnscreenText
import random


class Role():
    ATTACKER = 1
    DEFENDER = 2


class Phase(Enum):
    PREPARE = 1
    FIGHT = 2


INTERVAL = 0.025
NIGHTCOLOR = Vec4(0.15,0.15,0.3, 1)
DAWNCOLOR = Vec4(0.5, 0.4, 0.5, 1)
DAYCOLOR = Vec4(0.95,0.94,0.9, 1)
DUSKCOLOR = Vec4(0.8, 0.3, 0.4, 1)


class StateRunningSession(StateAbstract):
    def __init__(self):
        super(StateRunningSession, self).__init__("Game")

        self.towermanager = TowerManager(self)
        self.unitmanager = UnitManager(self)
        self.bulletmanager = BulletManager()

        self.curMoney = 0
        self.newMoney = 0

        self.phase = Phase.FIGHT
        self.role = Role.ATTACKER
        self.grace_p = False

        self.phasecounter = 0
        self.daycyle = 0
        self.canBuild = 0
        self.placewarpzone = 0
        self.started = False
        self.intervaltimer = INTERVAL
        self.BROADCAST_INTERVAL = 0.05
        self.PREP_TIME = 10.0
        self.FIGHT_TIME = 30.0
        self.GRACE_PERIOD = 3.0
        self.header = None
        self.splashtext = None
        self.footer = None
        self.cows = []
        self.round = 0
        self.ROUNDS = 20
        self.LIFES = 20
        self.lifes_left = 20
        self.lifes_lost = 0
        self.font = __builtin__.loader.loadFont("client/gui/HeadlinerNo.45 DEMO.ttf", pointSize= 60)
        self.light = DirectionalLight("light")#AmbientLight("ambientLight")
        self.light.setColor(NIGHTCOLOR)
        self.light.setShadowCaster(True, 512, 512)
        dlnp = __builtin__.base.render.attachNewNode(self.light)
        dlnp.setHpr(0, -60, 0)
        __builtin__.render.setLight(__builtin__.render.attachNewNode(self.light))

    def activate(self):
        # create gui
        __builtin__.base.guimanager.create_runningsession(self.role)

        self.unitmanager.activate()
        self.towermanager.activate()
        self.bulletmanager.activate()

        # set special keys
        self.accept("escape", __builtin__.base.guimanager.toggle_overlay)
        self.accept("mouse1", self.leftClick)

        __builtin__.base.inputmanager.activateKeyMovement()

        # create models
        self.bg = assethandling.createBackground()
        self.crosshair = assethandling.createCrosshair()
        self.selection = assethandling.createSelection()
        self.selection.setPos(0,0,0.08)
        self.selection.detachNode()
        self.zero = assethandling.createCrosshair()
        self.zero.setZ(0.01)
        self.phasecounter = 0
        self.daycyle = 0
        self.canBuild = 0
        self.placewarpzone = 0
        if self.header is not None:
            self.header.destroy()
        self.header = None
        if self.footer is not None:
            self.footer.destroy()
        if self.splashtext is not None:
            self.splashtext.destroy()
        self.splashtext = None
        self.footer = None
        self.round = 0
        self.lifes_left = self.LIFES
        self.lifes_lost = 0

    def prepare_build(self, name):
        if self.phase == Phase.FIGHT or self.grace_p:
            return
        self.canBuild = 1
        self.placewarpzone = 0
        if self.role == Role.DEFENDER:
            self.towermanager.prepare_cursor(name)
        if self.role == Role.ATTACKER:
            self.unitmanager.prepare_cursor(name)
        self.deselect()

    def prepare_warpzone(self):
        if self.phase == Phase.FIGHT or self.grace_p:
            return
        self.canBuild = 0
        self.placewarpzone = 1
        self.unitmanager.prepare_cursor("warpzone")
        self.deselect()

    def deselect(self):
        self.selection.detachNode()
        __builtin__.base.guimanager.destroy_selection()

    def deactivate(self):
        # clear gui
        __builtin__.base.guimanager.destroy_current_gui()

        # unset keys
        self.ignore("escape")
        self.ignore("mouse1")
        __builtin__.base.inputmanager.disableKeyMovement()

        # clear models
        self.bg.remove_node()
        self.crosshair.remove_node()
        self.zero.remove_node()

        self.unitmanager.deactivate()
        self.towermanager.deactivate()
        self.bulletmanager.deactivate()
        if self.splashtext is not None:
            self.splashtext.destroy()
        if self.header is not None:
            self.header.destroy()
        if self.footer is not None:
            self.footer.destroy()

        # reset cam
        __builtin__.base.camCenter.setPos(0, 0, 0)

    def update(self, dt):
        if self.started:
            self.daycyle -= dt
            self.phasecounter -= dt
            self.intervaltimer -= dt
            self.animate_light(INTERVAL)
            while self.intervaltimer <= 0:
                self.intervaltimer += INTERVAL
                self.animate_money()
            if self.phase == Phase.FIGHT:
                self.unitmanager.update(dt)
                self.towermanager.update(dt)
            else:
                self.unitmanager.update_placementcheck()
                self.towermanager.check_tower_positions()

            self.bulletmanager.update(dt)
            self.update_mouse()

    def animate_light(self, dt):
        if self.phase == Phase.PREPARE and not self.grace_p:
            progress = 1-self.phasecounter/self.PREP_TIME
            if progress < 0.3:
                progress *= (1/0.3)
                self.light.setColor(DAWNCOLOR*(1-progress) + DAYCOLOR*progress)
            elif progress >= 0.6 and progress < 0.85:
                progress -= 0.6
                progress *= (1/0.25)
                self.light.setColor(DAYCOLOR*(1-progress) + DUSKCOLOR*progress)
            elif progress >= 0.85:
                progress -= 0.85
                progress *= (1/0.3)
                self.light.setColor(DUSKCOLOR*(1-progress) + NIGHTCOLOR*progress)
        elif self.phase == Phase.PREPARE and self.grace_p:
            progress = (1-self.daycyle/(2*self.GRACE_PERIOD))
            self.light.setColor(DUSKCOLOR*(1-progress) + NIGHTCOLOR*progress)
        elif self.phase == Phase.FIGHT and self.grace_p:
            progress = 1-self.daycyle/self.GRACE_PERIOD
            self.light.setColor(NIGHTCOLOR*(1-progress) + DAWNCOLOR*progress)


    def animate_money(self):
        if self.curMoney is not self.newMoney:
            diff = self.newMoney - self.curMoney
            if diff == 0:
                return
            if abs(diff) > 50:
                self.curMoney += (abs(diff)/diff) * 3
            else:
                self.curMoney += abs(diff)/diff

    def select_tower(self, tower):
        self.selection.reparentTo(tower.towerNode)
        __builtin__.base.guimanager.create_tower_selection(tower)

    def select_unit(self, unit):
        self.selection.reparentTo(unit.unit_object)
        __builtin__.base.guimanager.create_unit_selection(unit)

    def leftClick(self):
        if self.phase == Phase.FIGHT or self.grace_p:
            return
        if self.canBuild:
            if self.role == Role.DEFENDER:
                self.towermanager.request_tower_placement(self.groundRayHit.getHitPos())
            if self.role == Role.ATTACKER:
                self.unitmanager.request_unit_placement(self.groundRayHit.getHitPos())
            return
        if self.placewarpzone:
            self.unitmanager.request_warpzone_placement(self.groundRayHit.getHitPos())
            return
        if self.role == Role.DEFENDER:
            self.towermanager.request_selection(self.groundRayHit.getHitPos())
        if self.role == Role.ATTACKER:
            self.unitmanager.request_selection(self.groundRayHit.getHitPos())
        return

    def update_settings(self, dec):
        self.BROADCAST_INTERVAL = dec.read_float()
        self.PREP_TIME = dec.read_int()
        self.FIGHT_TIME = dec.read_int()
        self.GRACE_PERIOD = dec.read_int()
        self.ROUNDS = dec.read_int()
        self.LIFES = dec.read_int()
        self.lifes_left = self.LIFES
        self.prepare_cows()

    def prepare_cows(self):
        print "prepare cows"
        counter = self.LIFES
        for i in range(0, 5, 1):
            for j in range(0, 5, 1):
                counter-=1
                print "cow created at"
                print (-2+4*i/4, -2.5+5*j/5)
                cow = __builtin__.loader.loadModel("client/models/cow")
                cow.reparentTo(__builtin__.render)
                cow.setScale(0.4, 0.4, 0.4)
                cow.setPos(-1.3+2.6*(i/4.0), -1.3+2.6*(j/4.0), 0)
                cow.setH(random.random()*360)
                self.cows.append(cow)
                if counter <= 0:
                    break


    def update_units(self, dec):
        self.deselect()
        self.unitmanager.destroy_units()
        l = dec.read_int()
        for i in range(0, l):
            self.unitmanager.build_unit(dec.read_string(),(dec.read_float(), dec.read_float()))

    def update_towers(self, dec):
        self.deselect()
        self.towermanager.destroy_towers()
        l = dec.read_int()
        for i in range(0, l):
            self.towermanager.build_tower(dec.read_string(),(dec.read_float(), dec.read_float()))

    def request_upgrade(self, obj, x):
        if self.role == Role.DEFENDER and self.phase == Phase.PREPARE:
            self.towermanager.request_upgrade(obj, x)
        if self.role == Role.ATTACKER and self.phase == Phase.PREPARE:
            self.unitmanager.request_upgrade(obj, x)

    def update_mouse(self):
        # Check to see if we can access the mouse. We need it to do anything else
        if __builtin__.base.mouseWatcherNode.hasMouse():
            # Get to and from pos in camera coordinates
            pMouse = __builtin__.base.mouseWatcherNode.getMouse()
            pFrom = Point3()
            pTo = Point3()
            __builtin__.base.camLens.extrude(pMouse, pFrom, pTo)

            # Transform to global coordinates
            pFrom = __builtin__.render.getRelativePoint(__builtin__.base.cam, pFrom)
            pTo = __builtin__.render.getRelativePoint(__builtin__.base.cam, pTo)

            # filter rayhits, we only need ground
            results = __builtin__.base.physicWorld.rayTestAll(pFrom, pTo)
            for hit in results.getHits():
                if hit.getNode().getName() == "Ground":
                    self.groundRayHit = hit
            self.crosshair.setPos(self.groundRayHit.getHitPos().getX(), self.groundRayHit.getHitPos().getY(), self.groundRayHit.getHitPos().getZ() + 0.06)
            #if self.phase == Phase.PREPARE and not self.grace_p:
            if self.role == Role.DEFENDER:
                self.towermanager.update_mouse(self.groundRayHit.getHitPos().getX(), self.groundRayHit.getHitPos().getY(), self.groundRayHit.getHitPos().getZ() + 0.06)
            elif self.role == Role.ATTACKER:
                self.unitmanager.update_mouse(self.groundRayHit.getHitPos().getX(), self.groundRayHit.getHitPos().getY(), self.groundRayHit.getHitPos().getZ() + 0.06)

    def grace_period(self):
        print "GRACE PERIOD"
        __builtin__.base.guimanager.hide_gui()
        self.grace_p = True
        if self.phase == Phase.PREPARE:
            self._pre_fight()
        else:
            self._pre_prepare()

    def _pre_fight(self):
        self.daycyle = self.GRACE_PERIOD
        self.canBuild = 0
        self.crosshair.detachNode()
        self.splashtext = OnscreenText(text = self._round_text(), fg=(1,1,1,1),font=self.font, pos=(0.0, 0.5), scale=0.05)
        if self.role == Role.ATTACKER:
            self.unitmanager.remove_cursor()
        else:
            self.towermanager.remove_cursor()
        self.deselect()

    def _pre_prepare(self):
        self.daycyle = self.GRACE_PERIOD
        self.round+=1
        if self.round == 1:
            self.header = OnscreenText(text = "YOU          ARE          THE", font=self.font,fg=(1,1,1,1), pos=(0.0, 0.6), scale=0.035)
            if self.role == Role.ATTACKER:
                self.splashtext = OnscreenText(text = "ATTACKER", font=self.font, fg=(1,1,1,1),pos=(0.0, 0.15), scale=0.1)
                self.footer = OnscreenText(text = ("STEAL        ALL        "+str(self.LIFES)+"        COWS"), fg=(1,1,1,1),font=self.font, pos=(0.0, -0.3), scale=0.05)
            else:
                self.splashtext = OnscreenText(text = "DEFENDER", font=self.font, fg=(1,1,1,1),pos=(0.0, 0.15), scale=0.1)
                self.footer = OnscreenText(text = ("SURVIVE        FOR        "+str(self.ROUNDS)+"        NIGHTS"), fg=(1,1,1,1),font=self.font, pos=(0.0, -0.3), scale=0.05)
        else:
            self.splashtext = OnscreenText(text = self._round_text(),fg=(1,1,1,1), font=self.font, pos=(0.0, 0.5), scale=0.05)
            self.footer = OnscreenText(text = (""+str(self.lifes_lost)+"      COWS       HAVE       BEEN       CAPTURED       LAST NIGHT"), fg=(1,1,1,1),font=self.font, pos=(0.0, 0.3), scale=0.03)

        self.towermanager.remove_all_projectiles()
        self.lifes_lost = 0

    def _round_text(self):
        string = ""
        if self.round == self.ROUNDS:
            if self.phase == Phase.PREPARE:
                string = "DUSK     OF     THE      LAST      NIGHT"
            else:
                string = "DAWN     OF     THE      LAST      DAY"
            return  string
        if self.phase == Phase.PREPARE:
            string = "DUSK     OF     THE     "+str(self.round)
        else:
            string = "DAWN     OF     THE     "+str(self.round)
        if self.round == 1:
            string += "st"
        elif self.round == 2:
            string += "nd"
        elif self.round == 3:
            string += "rd"
        else:
            string += "th"
        if self.phase == Phase.PREPARE:
            string += "     NIGHT"
        else:
            string += "     DAY"
        return string

    def phase_fight(self):
        print "FIGHT"
        __builtin__.base.guimanager.hide_gui()
        self.grace_p = False
        self.phase = Phase.FIGHT
        self.phasecounter = self.FIGHT_TIME
        self.splashtext.destroy()
        if self.role == Role.ATTACKER:
            self.unitmanager.fight()
            #self.towermanager.request_towerlist()
            #self.unitmanager.save()
        else:
            self.towermanager.fight()
            #self.unitmanager.request_unitlist()
            #self.towermanager.save()

    def phase_prepare(self):
        print "PREPARE"
        __builtin__.base.guimanager.show_gui()
        self.grace_p = False
        self.crosshair.reparentTo(__builtin__.render)
        if self.splashtext is not None:
            self.splashtext.destroy()
        if self.footer is not None:
            self.footer.destroy()
        if self.header is not None:
            self.header.destroy()
        self.phase = Phase.PREPARE
        self.phasecounter = self.PREP_TIME
        self.deselect()
        self.towermanager.remove_all_projectiles()
        if self.role == Role.ATTACKER:
            self.unitmanager.prepare()
            self.towermanager.destroy_towers()
        else:
            self.unitmanager.destroy_units()
            self.towermanager.prepare()

    def place_unit(self, dec):
        name = dec.read_string()
        pos = (dec.read_float(), dec.read_float())
        self.unitmanager.place_unit(name, pos)

    def place_warpzone(self, dec):
        pos = (dec.read_float(), dec.read_float())
        self.unitmanager.place_warpzone( pos)

    def place_tower(self, dec):
        name = dec.read_string()
        pos = (dec.read_float(), dec.read_float())
        self.towermanager.place_tower(name, pos)

    def shoot_projectile(self, dec):
        name = dec.read_string()
        pos = (dec.read_float(), dec.read_float())
        tar = dec.read_string()
        ind = dec.read_int()
        self.towermanager.shoot_projectile(name, pos, tar, ind)

    def remove_projectile(self, dec):
        ind = dec.read_int()
        try:
            self.towermanager.remove_projectile(ind)
        except IndexError:
            print "NETWORK ERROR: DESYNC: PROJECTILELIST OUTDATED"

    def destroy_unit(self, dec):
        ind = dec.read_int()
        unit = None
        try:
            unit = self.unitmanager.units[ind]
        except IndexError:
            print "NETWORK ERROR: DESYNC: SPECIFIED UNIT NOT FOUND"
        else:
            self.unitmanager.destroy_unit(unit)

    def destroy_tower(self, dec):
        ind = dec.read_int()
        tower = None
        try:
            tower = self.towermanager.placedTowers[ind]
        except IndexError:
            print "NETWORK ERROR: DESYNC: SPECIFIED UNIT NOT FOUND"
        else:
            self.towermanager.destroy_tower(tower)

    def upgrade(self, dec):
        ind = dec.read_int()
        upg = dec.read_int()
        if self.role == Role.ATTACKER:
            self.unitmanager.upgrade(ind, upg)
        else:
            self.towermanager.upgrade(ind, upg)

    def update_position(self, dec):
        positions = []
        for u in self.unitmanager.units:
            pos = (0, 0)
            try:
                pos = (dec.read_float(), dec.read_float())
            except AssertionError:
                print "NETWORK ERROR: DESYNC: UNIT LIST OUTDATED"
            positions.append(pos)
        self.unitmanager.update_position(positions)

    def update_projectiles(self, dec):
        positions = []
        for p in self.towermanager.projectiles:
            pos = (0,0)
            try:
                pos = (dec.read_float(), dec.read_float())
            except AssertionError:
                print "NETWORK ERROR: DESYNC: PROJECTILELIST OUTDATED"
            positions.append(pos)
        self.towermanager.update_projectiles(positions)

    def update_money(self, dec):
        self.newMoney = dec.read_int()

    def update_lifes(self, dec):
        lifes = dec.read_int()
        ll = self.lifes_left - lifes
        self.remove_cows(ll)
        self.lifes_lost += ll
        self.lifes_left = lifes
        print self.lifes_left

    def remove_cows(self, ll):
        for i in range(ll):
            self.cows.pop().removeNode()


    def update_unitdestination(self, dec):
        ind = dec.read_int()
        pos = (dec.read_float(), dec.read_float())
        self.unitmanager.units[ind].face(pos)

    def select(self, dec):
        i = dec.read_int()
        if self.role == Role.ATTACKER:
            self.select_unit(self.unitmanager.units[i])
        else:
            self.select_tower(self.towermanager.placedTowers[i])

    def prep_msg(self, type):
        enc = message.MessageEncoder()
        enc.reset()
        enc.write_string(type)
        enc.write_int(__builtin__.base.gamestatemanager.getState("CreateGame").ses_id)
        enc.write_int(self.role)
        return enc

    def send_message(self, msg):
        __builtin__.base.networkmanager.server_peer.send_reliable_sequenced(msg)

    def game_over(self, dec):
        player = dec.read_string()
        self.started = False
        if player == "attacker":
            if self.role == Role.ATTACKER:
                self.splashtext = OnscreenText(text = "YOU      WIN!", fg=(1,1,1,1),font=self.font, pos=(0.0, 0.15), scale=0.1)
            else:
                self.footer = OnscreenText(text = ("YOU      COULD      ONLY      SURVIVE       FOR     "+str(self.round - 1)+"        NIGHTS"), fg=(1,1,1,1),font=self.font, pos=(0.0, -0.0), scale=0.03)
                self.splashtext = OnscreenText(text = "YOU      LOSE", fg=(1,1,1,1),font=self.font, pos=(0.0, 0.15), scale=0.1)
            self.header = OnscreenText(text = ("ALL        "+str(self.LIFES)+"        COWS        HAVE        BEEN        CAPTURED!"), fg=(1,1,1,1),font=self.font, pos=(0.0, 0.6), scale=0.04)
        else:
            if self.role == Role.ATTACKER:
                self.splashtext = OnscreenText(text = "YOU      LOSE", font=self.font, fg=(1,1,1,1),pos=(0.0, 0.15), scale=0.1)
                self.footer = OnscreenText(text = ("YOU      COULD      ONLY       CAPTURE     "+str(self.LIFES - self.lifes_left)+"        COWS"), fg=(1,1,1,1),font=self.font, pos=(0.0, -0.0), scale=0.03)
            else:
                self.splashtext = OnscreenText(text = "YOU      WIN!", fg=(1,1,1,1),font=self.font, pos=(0.0, 0.15), scale=0.1)
            self.header = OnscreenText(text = (""+str(self.ROUNDS)+"        NIGHTS        HAVE        PASSED!"), fg=(1,1,1,1),font=self.font, pos=(0.0, 0.6), scale=0.04)




